'''
Created on 20 Dec 2009

@author: ai
'''

import os
import sys
import subprocess
from string import Template
import re
from textwrap import dedent

def execute_command(cmd):
    proc = subprocess.Popen(cmd,
                            shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    result = proc.communicate()[0]
    return result

class TestCase(object):
    def __init__(self, name, parent_class, package):
        self.name = name
        self.parent_class = parent_class
        self.package = package
        
    def get_fullname(self):
        return "%s.%s.%s" % (self.package, self.parent_class, self.name)
        
    def __repr__(self):
        return self.get_fullname() 
    
    def __str__(self):
        return self.get_fullname()

class CTagsWrapper(object):
    """ Convenience wrapper around Exuberant CTags.  Used to parse Java files
    and determine which class they implement and whether they extend an
    Android test case class that eventually has junit.framework.TestCase as
    an ancestor.
    """    
    
    CMD = Template("ctags --languages=java --fields=+iKz -f - --excmd=number ${fullpath}")
    TESTCASE_CLASSES = ["ActivityInstrumentationTestCase",
                        "ActivityInstrumentationTestCase2",
                        "ActivityTestCase",
                        "ActivityUnitTestCase",
                        "AndroidTestCase",
                        "ApplicationTestCase",
                        "InstrumentationTestCase",
                        "ProviderTestCase",
                        "ProviderTestCase2",
                        "ServiceTestCase",
                        "SingleLaunchActivityTestCase",
                        "SyncBaseInstrumentation"]    
    RE_PACKAGE_NAME = re.compile(("^(?P<package_name>\S+)\s+"
                                  "(?P<file_name>\S+)\s+"
                                  "(?P<line>\S+)\s+"
                                  "kind:package"))
    RE_TEST_CLASS = re.compile(("^(?P<class_name>\S+)\s+"
                                "(?P<file_name>\S+)\s+"
                                "(?P<line>\S+)\s+"
                                "kind:class\s+"
                                "inherits:(?P<inherits>%s)" % ('|'.join(TESTCASE_CLASSES), )))
    RE_TEST_NAME = re.compile(("^(?P<test_name>test\S+)\s+"
                               "(?P<file_name>\S+)\s+"
                               "(?P<line>\S+)\s+"
                               "kind:method\s+"
                               "class:(?P<parent_class>\S+)"))

    def __init__(self, fullpath):
        self.fullpath = fullpath
        assert(os.path.isfile(self.fullpath))
        self.result = None
        self.test_class = None
        self.inherits = None
        
    def execute_ctags(self, reexecute=False):
        if (not self.result) or (self.result and reexecute):
            cmd = self.CMD.substitute(fullpath=self.fullpath)
            self.result = execute_command(cmd).splitlines()
            return True
        else:
            return False
        
    def is_testcase_class(self):
        """ Determine if the main public class implemented in a .JAVA file 
        extends one of the Android testcase classes. Return True if it does,
        False if not.
        """
        assert(self.fullpath.lower().endswith(".java"))
        self.execute_ctags()
        self.test_class = None
        self.inherits = None
        self.package_name = None
        for line in self.result:
            m = self.RE_TEST_CLASS.match(line)
            if m:
                assert(not self.test_class)
                assert(not self.inherits)
                self.test_class = m.group('class_name')
                self.inherits = m.group('inherits')
                continue
            m = self.RE_PACKAGE_NAME.match(line)
            if m:
                assert(not self.package_name)
                self.package_name = m.group('package_name')
                continue
                
        if self.test_class and self.inherits and self.package_name:
            return True
        else:
            return False
        
    def get_tests(self):
        """ Get a list of all methods in a Android test case class file that
        start with 'test', which tells JUnit to consider the method a test
        case.  Return False if the current file is not a test case file.
        """        
        results = []
        assert(self.result)
        if self.is_testcase_class():
            for line in self.result:
                m = self.RE_TEST_NAME.match(line)
                if m and m.group('parent_class') == self.test_class:
                    tc = TestCase(m.group('test_name'), self.test_class, self.package_name)
                    results.append(tc)
            return results
        else:
            return False
                